package com.bridge.applets.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bridge.applets.model.bo.BaseResult;
import com.bridge.applets.model.bo.BridgeResultData;
import com.bridge.applets.model.entity.*;
import com.bridge.applets.model.enums.BridgeStateEnum;
import com.bridge.applets.model.enums.SampleTypeEnum;
import com.bridge.applets.model.vo.BodyList;
import com.bridge.applets.model.vo.PageBodyList;
import com.bridge.applets.model.vo.req.*;
import com.bridge.applets.model.vo.resp.BridgeCheckMeasureModelNumResp;
import com.bridge.applets.model.vo.resp.BridgeMeasureResp;
import com.bridge.applets.model.vo.resp.BridgeRecordResp;
import com.bridge.applets.service.*;
import com.bridge.applets.strategy.factory.BridgeFactory;
import com.bridge.applets.strategy.factory.FunctionModelFactory;
import com.bridge.applets.strategy.handler.bridge.BridgeHandler;
import com.bridge.applets.utils.HttpUtils;
import com.bridge.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BridgePcServiceImpl implements BridgePcService {

    @Autowired
    private BridgeFactory bridgeFactory;

    @Autowired
    private FunctionModelFactory functionModelFactory;
    @Autowired
    private BridgeNeuralNetworkService bridgeNeuralNetworkService;
    @Autowired
    private BridgeIdentifyCaeService bridgeIdentifyCaeService;
    @Autowired
    private BridgeMonitorAnalysisService bridgeMonitorAnalysisService;

    @Autowired
    private BridgeRecordService bridgeRecordService;

    @Autowired
    private BridgeMeasureModelService bridgeMeasureModelService;

    @Value("${analysis.url}")
    public String analysisUrl;

    @Value("${image.pre.url}")
    public String imagePreUrl;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBridge(BridgeAddReq req) {
        log.info("addBridge,param:{}", JSON.toJSONString(req));

        //根据判断 是否更新和新增
        BridgeRecord bridgeRecordExist = bridgeRecordService.queryById(req.getRecordId());
        if(Objects.isNull(bridgeRecordExist)){
            //查询桥梁编码 是否已经存在
            BridgeRecord recordByCode = bridgeRecordService.queryByCode(req.getBridgeCode());
            if(Objects.nonNull(recordByCode)){
                throw new ServiceException("桥梁编码已存在");
            }
        }else {
            if(!BridgeStateEnum.canDoAnalysis(bridgeRecordExist.getResultState())){
                throw new ServiceException("当前状态不可再编辑");
            }
        }

        /**
         * 校验输入数据
         * 保存桥梁模型数据
         * 保存桥梁数据
         */
        BridgeHandler bridgeHandler = bridgeFactory.getHandlerByType(req.getBridgeType().toString());
        //校验参数 并构建数据对象
        BridgeNeuralNetwork neuralNetwork = bridgeHandler.buildEntityNeuralNetwork(req);
        BridgeIdentifyCae identifyCae = bridgeHandler.buildEntityIdentifyCae(req);
        BridgeMonitorAnalysis monitorAnalysis = bridgeHandler.buildEntityMonitorAnalysis(req);

        if(Objects.isNull(bridgeRecordExist)){
            //每个模型数据 保存到表中
            bridgeNeuralNetworkService.save(neuralNetwork);
            bridgeIdentifyCaeService.save(identifyCae);
            bridgeMonitorAnalysisService.save(monitorAnalysis);

            //保存桥梁记录数据
            BridgeRecord bridgeRecord = new BridgeRecord();
            bridgeRecord.setBridgeCode(req.getBridgeCode());
            bridgeRecord.setBridgeName(req.getBridgeName());
            bridgeRecord.setBridgeType(req.getBridgeType());
            bridgeRecord.setRelationNeuralNetworkId(neuralNetwork.getId());
            bridgeRecord.setRelationIdentifyCaeId(identifyCae.getId());
            bridgeRecord.setRelationMonitorAnalysisId(monitorAnalysis.getId());
            bridgeRecord.setResultState(BridgeStateEnum.SAVE.getType());
            bridgeRecordService.save(bridgeRecord);
        }else {
            //每个模型数据 保存到表中
            neuralNetwork.setId(bridgeRecordExist.getRelationNeuralNetworkId());
            bridgeNeuralNetworkService.updateById(neuralNetwork);

            identifyCae.setId(bridgeRecordExist.getRelationIdentifyCaeId());
            bridgeIdentifyCaeService.updateById(identifyCae);

            monitorAnalysis.setId(bridgeRecordExist.getRelationMonitorAnalysisId());
            bridgeMonitorAnalysisService.updateById(monitorAnalysis);

            //保存桥梁记录数据
            BridgeRecord bridgeRecord = bridgeRecordExist;
            bridgeRecord.setBridgeCode(req.getBridgeCode());
            bridgeRecord.setBridgeName(req.getBridgeName());
            bridgeRecord.setBridgeType(req.getBridgeType());
            bridgeRecord.setRelationNeuralNetworkId(neuralNetwork.getId());
            bridgeRecord.setRelationIdentifyCaeId(identifyCae.getId());
            bridgeRecord.setRelationMonitorAnalysisId(monitorAnalysis.getId());
            bridgeRecord.setResultState(BridgeStateEnum.SAVE.getType());
            bridgeRecordService.updateById(bridgeRecord);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BridgeRecordResp analysisBridge(BridgeAnalysisReq req) {
        /**
         * 取出 桥梁数据
         * 构建 功能模型分析 对象
         * 调佣 各个功能模型，获取结果
         * 更新结果到数据表
         */
        BridgeRecord bridgeRecord = bridgeRecordService.queryById(req.getRecordId());
        if(Objects.isNull(bridgeRecord)){
            throw new ServiceException("桥梁记录信息不存在");
        }
        if(!BridgeStateEnum.canDoAnalysis(bridgeRecord.getResultState())){
            throw new ServiceException("当前状态不可进行分析处理");
        }

        BridgeNeuralNetwork neuralNetwork = bridgeNeuralNetworkService.queryById(bridgeRecord.getRelationNeuralNetworkId());

        BridgeIdentifyCae identifyCae = bridgeIdentifyCaeService.queryById(bridgeRecord.getRelationIdentifyCaeId());

        BridgeMonitorAnalysis monitorAnalysis = bridgeMonitorAnalysisService.queryById(bridgeRecord.getRelationMonitorAnalysisId());

        //校验样本
        String measurementSampleIds = monitorAnalysis.getMeasurementSampleIds();
        if(StringUtils.isEmpty(measurementSampleIds)){
            throw new ServiceException("样本数量还未设置");
        }
        List<Long> sampleIds = JSON.parseArray(measurementSampleIds, Long.class);
        if(CollectionUtils.isEmpty(sampleIds)){
            throw new ServiceException("样本数量还未设置");
        }

        List<BridgeMeasureModel> measureModels = bridgeMeasureModelService.queryByIds(sampleIds);
        if(measureModels.size()!=sampleIds.size()){
            throw new ServiceException("样本数量出现问题，请重新设置");
        }
        //List<BridgeMeasureModel> measureModels=new ArrayList<>();
        //获取工厂类
        BridgeHandler bridgeHandler = bridgeFactory.getHandlerByType(bridgeRecord.getBridgeType().toString());

        //构架 三个模型的 post数据
        Map<String, Object> paramNeuralNetwork = bridgeHandler.buildPostParamNeuralNetwork(neuralNetwork);
        Map<String, Object> paramIdentifyCae = bridgeHandler.buildPostParamIdentifyCae(identifyCae);

        Map<String, Object> paramMonitorAnalysis = bridgeHandler.buildPostParamMonitorAnalysis(monitorAnalysis);
        bridgeHandler.addMapParam(paramMonitorAnalysis,"sample_urls",measureModels.stream().map(BridgeMeasureModel::getFileUrl).collect(Collectors.toList()));
//        bridgeHandler.addMapParam(paramMonitorAnalysis,"sample_urls",new ArrayList<String>());
        //访问模型，获取模型结果
        Map<String, Object> paramMap = new HashMap();
        paramMap.put("recordId",bridgeRecord.getId());
        paramMap.put("bridgeType",bridgeRecord.getBridgeType());
        paramMap.put("bridgeName",bridgeRecord.getBridgeName());
        paramMap.put("bridgeCode",bridgeRecord.getBridgeCode());

        paramMap.put("measure",paramMonitorAnalysis);
        paramMap.put("cae",paramIdentifyCae);
        paramMap.put("preEvaluate",paramNeuralNetwork);

        BaseResult<BridgeResultData> baseResult = HttpUtils.postAnalysis(analysisUrl, paramMap);
        log.info("result:{}",baseResult);
        if(Objects.isNull(baseResult)||!baseResult.isSuccess()){
            throw new ServiceException("处理异常，请重试");
        }
//        if(bridgeRecord.getBridgeType()==3){
//            //斜拉桥 模拟测试结果
//            String resultData="{\"resultList\":[{\"zxjs\":\"1\",\"gypl\":\"0.266\",\"zxznb\":\"0.5\",\"mtzl\":\"8664.85\"},{\"zxjs\":\"2\",\"gypl\":\"0.322\",\"zxznb\":\"0.5\",\"mtzl\":\"12029.34\"},{\"zxjs\":\"3\",\"gypl\":\"0.41\",\"zxznb\":\"0.5\",\"mtzl\":\"12978.17\"}],\"imageList\":[{\"name\":\"桥梁模型图\",\"path\":\"images/2001/2001_0.png\"},{\"name\":\"第1阶振型图\",\"path\":\"images/2001/2001_1.png\"},{\"name\":\"第2阶振型图\",\"path\":\"images/2001/2001_2.png\"},{\"name\":\"第3阶振型图\",\"path\":\"images/2001/2001_3.png\"}]}";
//            baseResult.setData(JSON.parseObject(resultData,BridgeResultData.class));
//        }else if(bridgeRecord.getBridgeType()==1){
//            //梁桥  模拟测试结果
//            String resultData="{\"resultList\":[{\"zxjs\":\"1\",\"gypl\":\"3.661\",\"zxznb\":\"3.754\",\"mtzl\":\"864.41\"},{\"zxjs\":\"2\",\"gypl\":\"4.69\",\"zxznb\":\"3.218\",\"mtzl\":\"555.06\"},{\"zxjs\":\"3\",\"gypl\":\"6.844\",\"zxznb\":\"2.773\",\"mtzl\":\"388.23\"}],\"imageList\":[{\"name\":\"桥梁模型图\",\"path\":\"images/1001/1001_0.png\"},{\"name\":\"第1阶振型图\",\"path\":\"images/1001/1001_1.png\"},{\"name\":\"第2阶振型图\",\"path\":\"images/1001/1001_2.png\"},{\"name\":\"第3阶振型图\",\"path\":\"images/1001/1001_3.png\"}]}";
//            baseResult.setData(JSON.parseObject(resultData,BridgeResultData.class));
//        }
        bridgeRecord.setResultJson(JSON.toJSONString(baseResult.getData().getResultList()));
        bridgeRecord.setResultState(BridgeStateEnum.FINISH.getType());

        JSONObject extraObject=new JSONObject();
        List<BridgeRecordResp.ExtraImage> extraImageList = new ArrayList<>();
        baseResult.getData().getImageList().forEach(item->{
            BridgeRecordResp.ExtraImage extraImage=new BridgeRecordResp.ExtraImage();
            extraImage.setName(item.getName());
            extraImage.setUrl(imagePreUrl+item.getPath());
            extraImageList.add(extraImage);
        });

        extraObject.put("images",extraImageList);
        bridgeRecord.setExtraJson(extraObject.toJSONString());

//        //更新桥梁数据
        bridgeRecordService.updateById(bridgeRecord);

        BridgeRecordResp record=new BridgeRecordResp();

        record.setRecordId(bridgeRecord.getId());
        record.setBridgeName(bridgeRecord.getBridgeName());
        record.setBridgeCode(bridgeRecord.getBridgeCode());
        record.setBridgeType(bridgeRecord.getBridgeType());
        record.setResultStatus(bridgeRecord.getResultState());
        record.setResultJson(formatResultJson(bridgeRecord.getResultJson()));
        record.setImages(extraImageList);

        BridgeRecordResp.IdentifyCaeData identifyCaeData=new BridgeRecordResp.IdentifyCaeData();
        BeanUtils.copyProperties(identifyCae,identifyCaeData);
        record.setIdentifyCae(identifyCaeData);

        BridgeRecordResp.NeuralNetworkData neuralNetworkData=new BridgeRecordResp.NeuralNetworkData();
        BeanUtils.copyProperties(neuralNetwork,neuralNetworkData);
        record.setNeuralNetwork(neuralNetworkData);

        BridgeRecordResp.MonitorAnalysisData monitorAnalysisData=new BridgeRecordResp.MonitorAnalysisData();
        BeanUtils.copyProperties(monitorAnalysis,monitorAnalysisData);
        record.setMonitorAnalysis(monitorAnalysisData);

        return record;
    }

    @Override
    public PageBodyList<BridgeRecordResp> bridgeListByPage(BridgeRecodeListReq param) {
        log.info("bridgeListByPage-->param:{}",JSON.toJSONString(param));

        Page<BridgeRecord> page = bridgeRecordService.queryPage(param);
        PageBodyList<BridgeRecordResp> bodyList = new PageBodyList<>(0, new ArrayList<>());
        bodyList.setTotalSize((int) page.getTotal());
        List<BridgeRecord> records = page.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            List<Long> icIds = records.stream().map(BridgeRecord::getRelationIdentifyCaeId).collect(Collectors.toList());
            List<Long> nnIds = records.stream().map(BridgeRecord::getRelationNeuralNetworkId).collect(Collectors.toList());
            List<Long> maIds = records.stream().map(BridgeRecord::getRelationMonitorAnalysisId).collect(Collectors.toList());

            List<BridgeIdentifyCae> identifyCaeList = bridgeIdentifyCaeService.queryByIds(icIds);
            List<BridgeNeuralNetwork> neuralNetworkList = bridgeNeuralNetworkService.queryByIds(nnIds);
            List<BridgeMonitorAnalysis> monitorAnalysisList = bridgeMonitorAnalysisService.queryByIds(maIds);
            Map<Long, BridgeIdentifyCae> icMap = identifyCaeList.stream().collect(Collectors.toMap(BridgeIdentifyCae::getId, Function.identity()));
            Map<Long, BridgeNeuralNetwork> nnMap = neuralNetworkList.stream().collect(Collectors.toMap(BridgeNeuralNetwork::getId, Function.identity()));
            Map<Long, BridgeMonitorAnalysis> maMap = monitorAnalysisList.stream().collect(Collectors.toMap(BridgeMonitorAnalysis::getId, Function.identity()));

            List<String> bridgeCodes = page.getRecords().stream().map(BridgeRecord::getBridgeCode).collect(Collectors.toList());
            Map<String,Integer> measureModelNumMap= bridgeMeasureModelService.queryCountGroupCodeTypeByList(bridgeCodes);

            records.forEach(item -> {
                BridgeRecordResp record=new BridgeRecordResp();
                record.setRecordId(item.getId());
                record.setBridgeName(item.getBridgeName());
                record.setBridgeCode(item.getBridgeCode());
                record.setBridgeType(item.getBridgeType());
                record.setBridgeMeasureModelNumType0(measureModelNumMap.getOrDefault(getKey(item.getBridgeCode(), SampleTypeEnum.TYPE_0.getType()),0));
                record.setBridgeMeasureModelNumType1(measureModelNumMap.getOrDefault(getKey(item.getBridgeCode(), SampleTypeEnum.TYPE_1.getType()),0));
                record.setResultStatus(item.getResultState());
                record.setResultJson(formatResultJson(item.getResultJson()));
                record.setImages(getExtraImageList(item.getExtraJson()));

                BridgeIdentifyCae identifyCae = icMap.get(item.getRelationIdentifyCaeId());
                BridgeRecordResp.IdentifyCaeData identifyCaeData=new BridgeRecordResp.IdentifyCaeData();
                BeanUtils.copyProperties(identifyCae,identifyCaeData);
                record.setIdentifyCae(identifyCaeData);

                BridgeNeuralNetwork neuralNetwork = nnMap.get(item.getRelationNeuralNetworkId());
                BridgeRecordResp.NeuralNetworkData neuralNetworkData=new BridgeRecordResp.NeuralNetworkData();
                BeanUtils.copyProperties(neuralNetwork,neuralNetworkData);
                record.setNeuralNetwork(neuralNetworkData);

                BridgeMonitorAnalysis monitorAnalysis = maMap.get(item.getRelationMonitorAnalysisId());
                BridgeRecordResp.MonitorAnalysisData monitorAnalysisData=new BridgeRecordResp.MonitorAnalysisData();
                BeanUtils.copyProperties(monitorAnalysis,monitorAnalysisData);
                record.setMonitorAnalysis(monitorAnalysisData);


                bodyList.getBodyList().add(record);
            });
        }
        return bodyList;
    }

    private String formatResultJson(String resultJson){
        if(StringUtils.isEmpty(resultJson)){
            //不做处理
            return resultJson;
        }
        //针对数据，格式化一些数据
        List<BridgeResultData.Result> results = JSON.parseArray(resultJson, BridgeResultData.Result.class);
        //按照频率保留三位小数，阻尼比保留两位小数，模态质量保留一位小数
        for (BridgeResultData.Result result : results) {
            result.setGypl(formatDoubleStr(result.getGypl(),3));
            result.setZxznb(formatDoubleStr(result.getZxznb(),2));
            result.setMtzl(formatDoubleStr(result.getMtzl(),1));
        }
        return JSON.toJSONString(results);
    }

    private String formatDoubleStr(String oriValue, int decimal) {
        if(StringUtils.isEmpty(oriValue)){
            return oriValue;
        }
        return BigDecimal.valueOf(Double.valueOf(oriValue)).setScale(decimal, RoundingMode.DOWN).toString();
    }

    private String getKey(String bridgeCode,Integer sampleType){
        return bridgeCode+"-"+sampleType;
    }

    private List<BridgeRecordResp.ExtraImage> getExtraImageList(String extraJson) {
        if(StringUtils.isEmpty(extraJson)){
            return Collections.emptyList();
        }
        JSONObject parse = JSON.parseObject(extraJson);
        List<BridgeRecordResp.ExtraImage> extraImages= StringUtils.isNotEmpty(parse.getString("images")) ?parse.getJSONArray("images").toJavaList(BridgeRecordResp.ExtraImage.class):new ArrayList();
        return extraImages;
    }

    @Override
    public BridgeCheckMeasureModelNumResp checkMeasureModelNum(BridgeCheckMeasureModelNumReq req) {
        Integer count = bridgeMeasureModelService.queryCount(req.getBridgeCode());
        BridgeCheckMeasureModelNumResp resp=new BridgeCheckMeasureModelNumResp();
        resp.setCurrentMeasureNum(count);
        if(count>= req.getMeasureNum()){
            //满足测量数据样本个数
            return resp;
        }
        resp.setMsg("不满足测量样本个数，当前样本个数："+count);
        return resp;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBridge(BridgeDeleteReq req) {
        BridgeRecord bridgeRecord = bridgeRecordService.queryById(req.getRecordId());
        if(Objects.isNull(bridgeRecord)){
            throw new ServiceException("桥梁记录信息不存在");
        }
        //只删除 桥梁信息即可，其他信息都是根据此记录的关联id
        bridgeRecordService.deleteById(bridgeRecord.getId());
        bridgeMeasureModelService.deleteByBridgeCode(bridgeRecord.getBridgeCode());
    }

    @Override
    public BodyList<BridgeMeasureResp> bridgeMeasureListAll(BridgeMeasureReq req) {
        List<BridgeMeasureModel> bridgeMeasureModels = bridgeMeasureModelService.queryListAll(req);
        BodyList<BridgeMeasureResp> resp=new BodyList<>(new ArrayList<>());
        if(CollectionUtils.isEmpty(bridgeMeasureModels)){
            return resp;
        }

        for (BridgeMeasureModel bridgeMeasureModel : bridgeMeasureModels) {
            BridgeMeasureResp measure=new BridgeMeasureResp();
            measure.setId(bridgeMeasureModel.getId());
            measure.setBridgeCode(bridgeMeasureModel.getBridgeCode());
            measure.setBridgeName(bridgeMeasureModel.getBridgeName());
            measure.setFileName(bridgeMeasureModel.getFileName());
            measure.setSampleType(bridgeMeasureModel.getSampleType());

            resp.getBodyList().add(measure);
        }

        return resp;
    }

    @Override
    public void resetResult(BridgeResetResultReq req) {
        BridgeRecord bridgeRecord = bridgeRecordService.queryById(req.getRecordId());
        if(Objects.isNull(bridgeRecord)){
            throw new ServiceException("桥梁记录信息不存在");
        }
        //更新分析结果状态
        bridgeRecord.setResultState(BridgeStateEnum.SAVE.getType());
        bridgeRecordService.updateById(bridgeRecord);
    }

    @Override
    public BridgeRecordResp detailBridge(BridgeDetailReq req) {
        BridgeRecord bridgeRecord = bridgeRecordService.queryById(req.getRecordId());
        if(Objects.isNull(bridgeRecord)){
            throw new ServiceException("桥梁记录信息不存在");
        }

        BridgeNeuralNetwork neuralNetwork = bridgeNeuralNetworkService.queryById(bridgeRecord.getRelationNeuralNetworkId());

        BridgeIdentifyCae identifyCae = bridgeIdentifyCaeService.queryById(bridgeRecord.getRelationIdentifyCaeId());

        BridgeMonitorAnalysis monitorAnalysis = bridgeMonitorAnalysisService.queryById(bridgeRecord.getRelationMonitorAnalysisId());

        BridgeRecordResp record=new BridgeRecordResp();

        record.setRecordId(bridgeRecord.getId());
        record.setBridgeName(bridgeRecord.getBridgeName());
        record.setBridgeCode(bridgeRecord.getBridgeCode());
        record.setBridgeType(bridgeRecord.getBridgeType());
        record.setResultStatus(bridgeRecord.getResultState());
        record.setResultJson(bridgeRecord.getResultJson());
        record.setImages(getExtraImageList(bridgeRecord.getExtraJson()));

        BridgeRecordResp.IdentifyCaeData identifyCaeData=new BridgeRecordResp.IdentifyCaeData();
        BeanUtils.copyProperties(identifyCae,identifyCaeData);
        record.setIdentifyCae(identifyCaeData);

        BridgeRecordResp.NeuralNetworkData neuralNetworkData=new BridgeRecordResp.NeuralNetworkData();
        BeanUtils.copyProperties(neuralNetwork,neuralNetworkData);
        record.setNeuralNetwork(neuralNetworkData);

        BridgeRecordResp.MonitorAnalysisData monitorAnalysisData=new BridgeRecordResp.MonitorAnalysisData();
        BeanUtils.copyProperties(monitorAnalysis,monitorAnalysisData);
        record.setMonitorAnalysis(monitorAnalysisData);

        return record;
    }
}
